package com.andy.blog.common.utils.ip;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.andy.blog.common.config.RuoYiConfig;
import com.andy.blog.common.constant.BaseConstants;
import com.andy.blog.common.utils.http.HttpUtils;
import org.apache.commons.lang3.StringUtils;
import org.lionsoul.ip2region.xdb.Searcher;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.FileCopyUtils;

import java.io.IOException;
import java.io.InputStream;

/**
 * 获取地址类
 * Created by andy on 2023-04-05 11:38
 */
public class AddressUtils {

    private static final Logger logger = LoggerFactory.getLogger(AddressUtils.class);

    /**
     * IP地址查询
     */
    public static final String IP_URL = "http://whois.pconline.com.cn/ipJson.jsp";

    /**
     * 未知地址
     */
    public static final String UNKNOWN = "XX XX";

    protected static Searcher searcher;

    static {
        try (InputStream resourceAsStream = AddressUtils.class.getClassLoader().getResourceAsStream("ip2region/ip2region.xdb")) {
            if (null != resourceAsStream) {
                byte[] cBuff = FileCopyUtils.copyToByteArray(resourceAsStream);
                searcher = Searcher.newWithBuffer(cBuff);
            }
        } catch (IOException e) {
            logger.error("AddressUtils.error:", e);
        }
    }

    /**
     * 在线获取ip定位
     *
     * @param ip ip
     * @return
     */
    public static String getRealAddressByIP(String ip) {
        // 内网不查询
        if (IpUtils.internalIp(ip)) {
            return "内网IP";
        }
        if (RuoYiConfig.isAddressEnabled()) {
            try {
                String rspStr = HttpUtils.sendGet(IP_URL, "ip=" + ip + "&json=true", BaseConstants.GBK);
                if (StringUtils.isBlank(rspStr)) {
                    logger.error("获取地理位置异常 {}", ip);
                    return UNKNOWN;
                }
                JSONObject obj = JSON.parseObject(rspStr);
                String region = obj.getString("pro");
                String city = obj.getString("city");
                return String.format("%s|%s", region, city);
            } catch (Exception e) {
                logger.error("获取地理位置异常 {}", ip);
            }
        }
        return UNKNOWN;
    }

    /**
     * 通过ip2region获取位置
     *
     * @param ip ip
     * @return 国家|区域|省份|城市|ISP_
     */
    public static String getRealAddressByIp2region(String ip) {
        // 内网不查询
        if (IpUtils.internalIp(ip)) {
            return "内网IP";
        }
        String address = getRegion(ip);
        // 若没有获取到,采用ip查询方式获取
        if (StringUtils.isBlank(address)) {
            return getRealAddressByIP(ip);
        }

        String[] addressArray = address.split("\\|");
        return String.format("%s|%s|%s|%s", addressArray[0], addressArray[2], addressArray[3], addressArray[4]);
    }

    /**
     * 获取省
     */
    public static String getProvinceByIp(String ip) {
        // 内网不查询
        if (IpUtils.internalIp(ip)) {
            return "内网IP";
        }
        String address = getRegion(ip);
        // 若没有获取到,采用ip查询方式获取
        if (StringUtils.isBlank(address)) {
            return getRealAddressByIP(ip);
        }

        String[] addressArray = address.split("\\|");
        return addressArray[0];
    }

    /**
     * 离线根据ip获取位置--2.X
     *
     * @param ip ip
     * @return 国家|区域|省份|城市|ISP_
     */
    public static String getRegion(String ip) {
        long ipLong = ipToLong(ip);
        try {
            String search = searcher.search(ipLong);
            if (StringUtils.isNotBlank(search)) {
                return search;
            }
        } catch (Exception e) {
            logger.error("AddressUtils.error:", e);
        }
        return StringUtils.EMPTY;
    }

    /**
     * ip转long
     *
     * @param ip ip
     * @return long
     */
    public static long ipToLong(String ip) {
        String[] split = ip.split("\\.");
        long i1 = 0L;
        i1 += (long) Integer.parseInt(split[0]) << 24;
        i1 += (long) Integer.parseInt(split[1]) << 16;
        i1 += (long) Integer.parseInt(split[2]) << 8;
        return i1 + Integer.parseInt(split[3]);
    }
}
